package com.shm.leetcode;

import java.util.LinkedList;
import java.util.Queue;

/**
 * 111. 二叉树的最小深度
 * 给定一个二叉树，找出其最小深度。
 *
 * 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
 *
 * 说明: 叶子节点是指没有子节点的节点。
 *
 * 示例:
 *
 * 给定二叉树 [3,9,20,null,null,15,7],
 *
 *     3
 *    / \
 *   9  20
 *     /  \
 *    15   7
 * 返回它的最小深度  2.
 * @author SHM
 */
public class MinDepthTree {
    /**
     * 方法一：深度优先搜索
     * 思路及解法
     *
     * 首先可以想到使用深度优先搜索的方法，遍历整棵树，记录最小深度。
     *
     * 对于每一个非叶子节点，我们只需要分别计算其左右子树的最小叶子节点深度。这样就将一个大问题转化为了小问题，可以递归地解决该问题。
     *
     * 复杂度分析
     *
     * 时间复杂度：O(N)O(N)，其中 NN 是树的节点数。对每个节点访问一次。
     *
     * 空间复杂度：O(H)O(H)，其中 HH 是树的高度。空间复杂度主要取决于递归时栈空间的开销，最坏情况下，树呈现链状，空间复杂度为 O(N)O(N)。平均情况下树的高度与节点数的对数正相关，空间复杂度为 O(\log N)O(logN)。
     * 作者：LeetCode-Solution
     * 链接：https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/solution/er-cha-shu-de-zui-xiao-shen-du-by-leetcode-solutio/
     *
     * 思路：
     * 很多人写出的代码都不符合 1,2 这个测试用例，是因为没搞清楚题意
     *
     * 题目中说明:叶子节点是指没有子节点的节点，这句话的意思是 1 不是叶子节点
     *
     * 题目问的是到叶子节点的最短距离，所以所有返回结果为 1 当然不是这个结果
     *
     * 另外这道题的关键是搞清楚递归结束条件
     *
     * 叶子节点的定义是左孩子和右孩子都为 null 时叫做叶子节点
     * 当 root 节点左右孩子都为空时，返回 1
     * 当 root 节点左右孩子有一个为空时，返回不为空的孩子节点的深度
     * 当 root 节点左右孩子都不为空时，返回左右孩子较小深度的节点值
     *
     * 作者：reals
     * 链接：https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/solution/li-jie-zhe-dao-ti-de-jie-shu-tiao-jian-by-user7208/
     * @param root
     * @return
     */
    public int minDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        //这道题递归条件里分为三种情况
        //1.左孩子和有孩子都为空的情况，说明到达了叶子节点，直接返回1即可
        if(root.left == null && root.right==null){
            return 1;
        }
        //2.如果左孩子和由孩子其中一个为空，那么需要返回比较大的那个孩子的深度
        int left = minDepth(root.left);
        int right = minDepth(root.right);
        if (root.left==null || root.right==null){
            return Math.max(left,right)+1;
        }
        //3.最后一种情况，也就是左右孩子都不为空，返回最小深度+1即可
        return Math.min(left,right)+1;
    }


    class QueueNode{
        TreeNode node;
        int depth;
        QueueNode(TreeNode node,int depth){
            this.depth = depth;
            this.node = node;
        }
    }

    /**
     * 方法二：广度优先搜索
     * 思路及解法
     *
     * 同样，我们可以想到使用广度优先搜索的方法，遍历整棵树。
     *
     * 当我们找到一个叶子节点时，直接返回这个叶子节点的深度。广度优先搜索的性质保证了最先搜索到的叶子节点的深度一定最小。
     *
     * 复杂度分析
     *
     * 时间复杂度：O(N)O(N)，其中 NN 是树的节点数。对每个节点访问一次。
     *
     * 空间复杂度：O(N)O(N)，其中 NN 是树的节点数。空间复杂度主要取决于队列的开销，队列中的元素个数不会超过树的节点数。
     * 作者：LeetCode-Solution
     * 链接：https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/solution/er-cha-shu-de-zui-xiao-shen-du-by-leetcode-solutio/
     * @param root
     * @return
     */
    public int minDepth_2(TreeNode root){
        if (root == null){
            return 0;
        }
        if (root.left == null && root.right==null){
            return 1;
        }
        Queue<QueueNode> queue = new LinkedList<>();
        QueueNode queueNode = new QueueNode(root, 1);
        queue.offer(queueNode);
        while (!queue.isEmpty()){
            QueueNode poll = queue.poll();
            TreeNode node = poll.node;
            int depth = poll.depth;
            if (node.right==null&&node.left==null){
                return depth;
            }
            if (node.right!=null){
                queue.offer(new QueueNode(node.right, depth+1));
            }
            if (node.left !=null){
                queue.offer(new QueueNode(node.left,depth+1));
            }
        }
        return 0;
    }
}
